import path from 'path';
import { findFiles } from '../tools/vendor.js';
import { StructDef, StructWrap } from '../typings/index.js';
import { BaseStructParser, IConvInfo } from './BaseStructParser.js';
import { toolchain } from '../tools/toolchain.js';

export class JsonStructParser extends BaseStructParser {

    private static ins: JsonStructParser;
    public static get Instance(): JsonStructParser {
        if(!JsonStructParser.ins) JsonStructParser.ins = new JsonStructParser();
        return JsonStructParser.ins;
    }

    public async readStructs(root: string, includeAdditional: boolean): Promise<string[]> {
        this.reset();
        const files = findFiles(root, ['.xml'], '+');
        for(const f of files) {
            if(f.endsWith('AdditionalMember.xml')) continue;

            await this.readStructFile(f, false);
        }
        // 再附加AdditionalMember.xml
        if (includeAdditional) {
            await this.readStructFile(path.join(root, 'AdditionalMember.xml'), true);
        }
        await this.checkErrors();

        return this.m_cliStructs.sort();
    }

    protected isClientStruct(struct: StructDef): boolean {
        return "1" === struct._attributes.rootflag && struct._attributes.name.endsWith('Flash');
    }

    protected makeStructComment(structName: string, wrap: StructWrap): string {
        const convInfo = this.getConvInfo(structName);
        if (convInfo != null) {
            let c = '/**\n';
            if (wrap.def._attributes.desc) {
                c += ` * ${wrap.def._attributes.desc}\n`;
            }
            c += ` * @xlsx ${convInfo.xlsx}\n`;
            c += ` * @sheet ${convInfo.sheet}\n`;
            c += ` * @xml ${convInfo.xml}\n`;
            c += ` * @bin ${convInfo.bin}\n`;
            if (wrap.def._attributes.sortPlusKeys) {
                const rawSkeys = wrap.def._attributes.sortPlusKeys.split(',');
                const orders = rawSkeys.map((v) => v.endsWith('-') ? 'desc' : 'asc');
                const skeys = rawSkeys.map((v, i) => `{@link ${v.replace(/[\+-]+$/, '')}}` + (orders[i] == 'desc' ? '(降序)' : '(升序)'));
                c += ` * @sortPlusKeys ${skeys.join(', ')}\n`;
            } else if (wrap.def._attributes.sortkey) {
                c += ` * @sortkey {@link ${wrap.def._attributes.sortkey}}, ${convInfo.sort == 'Desc' ? '降序' : '升序'}\n`;
            }
            // 增加预处理提示
            // dropFieldRules
            // 解析diff规则
            const dfRule = toolchain.projCfg.xml2json?.dropFieldRules?.find((v) => v.name == structName);
            if (dfRule) {
                c += ` * @dropFieldRule 以下字段启用了丢弃预处理：${dfRule.dropKeys.map((v) => `{@link ${v.name}}`).join(', ')}\n`;
            }
            const tsRule = toolchain.projCfg.xml2json?.treeShakeRules?.find((v) => v.name == structName);
            if (tsRule) {
                const tsTests = tsRule.tests.map((v) => (v.rule == 'if_equal' ? '其值等于' : '其值不等于') + v.select).join('、')
                c += ` * @treeShakeRule 当{@link ${tsRule.if_key}}满足以下条件：${tsTests}，仅保留以下字段：${tsRule.then_keep.split(',').map((v) => `{@link ${v}}`).join(', ')}\n`;
            }
            const dfvRule = toolchain.projCfg.xml2json?.diffValueRules?.find((v) => v.name == structName);
            if (dfvRule) {
                c += ` * @diffValueRule 以下字段预先经过diff处理：${dfvRule.diff_fields.map((v) => `{@link ${v.name}}`).join(', ')}\n`;
            }
            
            c += '*/\n';
            return c;
        }
        return super.makeStructComment(structName, wrap);
    }

    public getConvInfosByXls(xls: string): IConvInfo[] {
        const xlsName = path.basename(xls);

        const infos: IConvInfo[] = [];
        for (const structName in this.convMap) {
            const info = this.convMap[structName];
            if (info.xlsx == xlsName) {
                infos.push(info);
            }
        }

        return infos;
    }
}
